﻿using Centny.PdfReader.Graphics;
using Centny.PdfReader.Pdf;
using Centny.PdfReader.Types;
using Centny.PdfReader.util;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

namespace Centny.PdfReader.IO
{
    public interface PdfOperatorExecuter
    {
        PdfStreamLexer Lexer { get; set; }
        String Operator { get; }
        void Execute(PdfOperator op);
    }
    public class PdfStreamLexer : PdfLexer
    {
        private static Dictionary<String, PdfOperatorExecuter> _Executers = new Dictionary<String, PdfOperatorExecuter>();
        public static Dictionary<String, PdfOperatorExecuter> Executers
        {
            get
            {
                return _Executers;
            }
        }
        public static void AddExecuter(PdfOperatorExecuter oe)
        {
            Executers[oe.Operator] = oe;
        }
        public static void RegisterOperatorExecuter(Assembly asm)
        {
            TypeInfo oetyi = typeof(PdfOperatorExecuter).GetTypeInfo();
            foreach (TypeInfo t in asm.DefinedTypes)
            {
                if (!oetyi.IsAssignableFrom(t))
                {
                    continue;
                }
                if (t.IsAbstract)
                {
                    continue;
                }
                //RuntimeHelpers.RunClassConstructor(t.AsType().TypeHandle);
                try
                {
                    PdfOperatorExecuter oe = (PdfOperatorExecuter)Activator.CreateInstance(t.AsType());
                    AddExecuter(oe);
                }
                catch (Exception e)
                {
                    System.Diagnostics.Debug.WriteLine("Register operator(" + t.FullName + ") error:" + e.ToString());
                }
            }
        }
        static PdfStreamLexer()
        {
            PdfStreamLexer.RegisterOperatorExecuter(typeof(PdfOperatorExecuter).GetTypeInfo().Assembly);
        }
        //
        public PdfStreamLexer(Stream s)
            : base(s)
        {
            //this.Ctm = new Matrix();
            //this.HorizontalScaling = new PdfDouble(100);
        }
        //public PdfClassObject CObj { get; set; }
        public void Process()
        {
            //PdfTimer t = new PdfTimer();
            while (!this.Eof)
            {
                PdfOperator op = this.NextOperator();
                if (op == null)
                {
                    continue;
                }
                //t.Beg();
                this.Process(op);
                //t.End(op.Operator);
            }
            //t.Total("PdfOperator-T:");
        }
        public PdfOperator NextOperator()
        {
            List<PdfClass> args = new List<PdfClass>();
            PdfOperator op = null;
            while (!this.Eof)
            {
                PdfClass obj = this.Next();
                if (obj is PdfOperator)
                {
                    op = obj as PdfOperator;
                    break;
                }
                else
                {
                    args.Add(obj);
                }
            }
            if (op != null && args.Count > 0)
            {
                op.Args = args;
            }
            return op;
        }
        public virtual void Process(PdfOperator op)
        {
            if (PdfStreamLexer.Executers.ContainsKey(op.Operator))
            {
                PdfOperatorExecuter oe = PdfStreamLexer.Executers[op.Operator];
                oe.Lexer = this;
                oe.Execute(op);
            }
            else
            {
                System.Diagnostics.Debug.WriteLine("not implemented operator:" + op.Operator);
            }
        }
        //
        public virtual void SetCtm(PdfMatrix ctm)
        {
            //ctm.Multiply(this.GraphicsState.Ctm, this.GraphicsState.Ctm);
            this.GraphicsState.Ctm = ctm;
        }
        public virtual void OnBegText()
        {
            this.GraphicsState.TextState.TextMatrix = new PdfMatrix();
            this.GraphicsState.TextState.LineMatrix = new PdfMatrix();
        }
        public virtual void OnEndText()
        {
            this.GraphicsState.TextState.TextMatrix = null;
            this.GraphicsState.TextState.LineMatrix = null;
        }
        public virtual void SetFontNameSize(PdfName fname, PdfDouble fsize)
        {
            this.GraphicsState.TextState.FontName = fname;
            this.GraphicsState.TextState.Font.FontSize = fsize.Val;
        }
        public virtual void SetHorScaling(PdfDouble hs)
        {
            this.GraphicsState.TextState.HorizontalScaling = hs.Val;
        }
        public virtual void OnDraw(PdfString text) { }
        //
        private PdfGraphicsState _GraphicsState = new PdfGraphicsState();
        public PdfGraphicsState GraphicsState
        {
            get
            {
                return this._GraphicsState;
            }
        }

        private Stack<PdfGraphicsState> _GS = new Stack<PdfGraphicsState>();
        public void Store()
        {
            this._GS.Push(this._GraphicsState.Clone() as PdfGraphicsState);
        }

        public void Restore()
        {
            this._GraphicsState = this._GS.Pop();
        }
    }
}
